<!--
  Copyright (c) 2006-2013, JGraph Ltd
  
  An implementation for export which creates data-uris for all images
  on the client-side. This is used to avoid access problems with servlet.
-->
<html>
<head>
	<title>Export example for mxGraph</title>

	<!-- Sets the basepath for the library if not in same directory -->
	<script type="text/javascript">
		mxBasePath = '/mxgraph/javascript/src';
	</script>

	<!-- Loads and initializes the library -->
	<script type="text/javascript" src="/mxgraph/javascript/src/js/mxClient.js"></script>
	
	<script type="text/vbscript">
     Function mxUtilsBinaryToArray(Binary)
         Dim i
         ReDim byteArray(LenB(Binary))
         For i = 1 To LenB(Binary)
             byteArray(i-1) = AscB(MidB(Binary, i, 1))
         Next
         mxUtilsBinaryToArray = byteArray
     End Function
	</script>
	
	<!-- Example code -->
	<script type="text/javascript">
	// Program starts here. Creates a sample graph in the
	// DOM node with the specified ID. This function is invoked
	// from the onLoad event handler of the document (see below).
	function main(container)
	{
		// Checks if the browser is supported
		if (!mxClient.isBrowserSupported())
		{
			// Displays an error message if the browser is not supported.
			mxUtils.error('Browser is not supported!', 200, false);
		}
		else
		{
			// Disables the built-in context menu
			mxEvent.disableContextMenu(container);
			
			// Creates the graph inside the given container
			var graph = new mxGraph(container);

			// Enables rubberband selection
			new mxRubberband(graph);
			
			// Gets the default parent for inserting new cells. This
			// is normally the first child of the root (ie. layer 0).
			var parent = graph.getDefaultParent();
							
			// Adds cells to the model in a single step
			graph.getModel().beginUpdate();
			try
			{
				var v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 80,
					'shape=image;image=http://www.jgraph.com/images/mxgraph.gif');
				var v1 = graph.insertVertex(parent, null, 'Hello,', 200, 20, 80, 80,
				'shape=image;image=/mxgraph/javascript/examples/images/add.png');
				var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 40, 40,
					'shape=image;image=/mxgraph/javascript/examples/images/add.png');
				var v2 = graph.insertVertex(parent, null, 'World!', 20, 150, 40, 40,
					'shape=image;image=/mxgraph/javascript/examples/images/add.png');
				var e1 = graph.insertEdge(parent, null, '', v1, v2);
			}
			finally
			{
				// Updates the display
				graph.getModel().endUpdate();
			}
			
			// Exporting to SVG using EchoServlet
			document.body.appendChild(mxUtils.button('Export SVG', function()
			{
				var background = '#ffffff';
				var scale = 1;
				var border = 1;
				
				var imgExport = new mxImageExport();
				var bounds = graph.getGraphBounds();
				var vs = graph.view.scale;

				// Prepares SVG document that holds the output
				var svgDoc = mxUtils.createXmlDocument();
				var root = (svgDoc.createElementNS != null) ?
			    		svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
			    
				if (background != null)
				{
					if (root.style != null)
					{
						root.style.backgroundColor = background;
					}
					else
					{
						root.setAttribute('style', 'background-color:' + background);
					}
				}
			    
				if (svgDoc.createElementNS == null)
				{
			    	root.setAttribute('xmlns', mxConstants.NS_SVG);
			    	root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
				}
				else
				{
					// KNOWN: Ignored in IE9-11, adds namespace for each image element instead. No workaround.
					root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
				}
				
				root.setAttribute('width', (Math.ceil(bounds.width * scale / vs) + 2 * border) + 'px');
				root.setAttribute('height', (Math.ceil(bounds.height * scale / vs) + 2 * border) + 'px');
				root.setAttribute('version', '1.1');
				
			    // Adds group for anti-aliasing via transform
				var group = (svgDoc.createElementNS != null) ?
						svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g');
				group.setAttribute('transform', 'translate(0.5,0.5)');
				root.appendChild(group);
				svgDoc.appendChild(root);

			    // Renders graph. Offset will be multiplied with state's scale when painting state.
				var svgCanvas = new mxSvgCanvas2D(group);
				svgCanvas.translate(Math.floor((border / scale - bounds.x) / vs), Math.floor((border / scale - bounds.y) / vs));
				svgCanvas.scale(scale / vs);
				imgExport.drawState(graph.getView().getState(graph.model.root), svgCanvas);

				var xml = encodeURIComponent(mxUtils.getXml(root));
				new mxXmlRequest('/Echo', 'filename=export.svg&format=svg' + '&xml=' + xml).simulate(document, '_blank');
			}));

			function exportFile(format)
			{
				var bg = '#ffffff';
				var scale = 1;
				var b = 1;
				
				var imgExport = new mxImageExport();
				var bounds = graph.getGraphBounds();
				var vs = graph.view.scale;
				
	        	// New image export
				var xmlDoc = mxUtils.createXmlDocument();
				var root = xmlDoc.createElement('output');
				xmlDoc.appendChild(root);
				
			    // Renders graph. Offset will be multiplied with state's scale when painting state.
				var xmlCanvas = new mxXmlCanvas2D(root);
				xmlCanvas.translate(Math.floor((b / scale - bounds.x) / vs), Math.floor((b / scale - bounds.y) / vs));
				xmlCanvas.scale(scale / vs);
				
				var waitCounter = 1;

				function done(immediate)
				{
					var fn = function()
					{
						var w = Math.ceil(bounds.width * scale / vs + 2 * b);
						var h = Math.ceil(bounds.height * scale / vs + 2 * b);
						
						var xml = mxUtils.getXml(root);

						if (bg != null)
						{
							bg = '&bg=' + bg;
						}
						
						new mxXmlRequest('/Export', 'filename=export.' + format + '&format=' + format +
		        			bg + '&w=' + w + '&h=' + h + '&xml=' + encodeURIComponent(xml)).
		        			simulate(document, '_blank');
					}
					
					if (immediate)
					{
						fn();
					}
					else
					{
						// New user context needed to avoid popup blocker
						var btn = mxUtils.button('Download ' + format.toUpperCase(), function()
						{
							btn.parentNode.removeChild(btn);
							fn();
						});
					
						document.body.appendChild(btn);
					}
				}
				
				// Lookup for queue of functions or data URI
				var imageLookup = new Object();
				
				// Called when async function has terminated
				function leave(immediate)
				{
					waitCounter--;
					
					if (waitCounter == 0)
					{
						done(immediate);
					}
				}
				
				// Enters function into barrier
				function enter(fn)
				{
					waitCounter++;
					fn();
				}
				
				xmlCanvas.image = function(x, y, w, h, src, aspect, flipH, flipV)
				{
					src = this.converter.convert(src);
					
					// LATER: Add option for embedding images as base64.
					var elem = this.createElement('image');
					elem.setAttribute('x', this.format(x));
					elem.setAttribute('y', this.format(y));
					elem.setAttribute('w', this.format(w));
					elem.setAttribute('h', this.format(h));
					elem.setAttribute('src', src);
					elem.setAttribute('aspect', (aspect) ? '1' : '0');
					elem.setAttribute('flipH', (flipH) ? '1' : '0');
					elem.setAttribute('flipV', (flipV) ? '1' : '0');
					this.root.appendChild(elem);
					
					if (src.substring(5) != 'data:')
					{
						var img = imageLookup[src];
						
						if (img != 'error')
						{
							if (img instanceof Array)
							{
								// Adding element to queue
								img.push(function(value)
								{
									elem.setAttribute('src', value);
								});
							}
							else if (img instanceof String)
							{
								// Using cached value
								elem.setAttribute('src', img);
							}
							else if (img == null)
							{
								imageLookup[src] = [function(value)
								{
									elem.setAttribute('src', value);
								}];
								
								// Enters function into barrier, calls leave when done
								enter(function()
								{
									try
									{
										var xhr = new mxXmlRequest(src, null, 'GET');
										xhr.setBinary(true);
										xhr.send(function(req)
										{
											var data = null;
											
											if (mxClient.IS_IE)
											{
												// This requires BinaryToArray VB script in the page
												var bin = mxUtilsBinaryToArray(req.request.responseBody).toArray();
												var tmp = new Array(bin.length);
												
												for (var i = 0; i < bin.length; i++)
												{
													tmp[i] = String.fromCharCode(bin[i]);
												}
											}
											else
											{
												var bin = req.getText();
												var tmp = new Array(bin.length);
												
												for (var i = 0; i < bin.length; i++)
												{
													tmp[i] = String.fromCharCode(bin.charCodeAt(i) & 0xFF);
												}
											}
											
											data = tmp.join('');
											
											if (data != null && data.length > 0)
											{
												// TODO: Check support for SVG
												var ext = src.substring(src.lastIndexOf('.') + 1, src.length);
												img = 'data:image/' + ext.toLowerCase() + ';base64,' + btoa(data);
												
												// Array of functions
												for (var i = 0; i < imageLookup[src].length; i++)
												{
													imageLookup[src][i](img);
												}
												
												imageLookup[src] = img;
											}
											else
											{
												imageLookup[src] = 'error';
											}
											
											leave();
										},
										function(req)
										{
											imageLookup[src] = 'error';
											
											leave();
										});
									}
									catch (e)
									{
										imageLookup[src] = 'error';
										
										leave();
									}
								});
							}
						}
					}
				};
				
			    imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas);
			    leave(true);
			}
			
			// Exporting to bitmap using ExportServlet
			document.body.appendChild(mxUtils.button('Export PNG', function()
			{
				exportFile('png');
			}));
			
			// Exporting to PDF using ExportServlet
			document.body.appendChild(mxUtils.button('Export PDF', function()
			{
				exportFile('pdf');
			}));
		}
	};

	//
	// Base64 bota/atob shim
	//
	(function() {
		 
		var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		var base64DecodeChars = new Array(
		    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
		    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
		    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
		    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
		    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
		    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
		    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
		    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
		 
		function base64encode(str) {
		    var out, i, len;
		    var c1, c2, c3;
		 
		    len = str.length;
		    i = 0;
		    out = "";
		    while(i < len) {
		  c1 = str.charCodeAt(i++) & 0xff;
		  if(i == len)
		  {
		      out += base64EncodeChars.charAt(c1 >> 2);
		      out += base64EncodeChars.charAt((c1 & 0x3) << 4);
		      out += "==";
		      break;
		  }
		  c2 = str.charCodeAt(i++);
		  if(i == len)
		  {
		      out += base64EncodeChars.charAt(c1 >> 2);
		      out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
		      out += base64EncodeChars.charAt((c2 & 0xF) << 2);
		      out += "=";
		      break;
		  }
		  c3 = str.charCodeAt(i++);
		  out += base64EncodeChars.charAt(c1 >> 2);
		  out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
		  out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
		  out += base64EncodeChars.charAt(c3 & 0x3F);
		    }
		    return out;
		}
		 
		function base64decode(str) {
		    var c1, c2, c3, c4;
		    var i, len, out;
		 
		    len = str.length;
		    i = 0;
		    out = "";
		    while(i < len) {
		  /* c1 */
		  do {
		      c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
		  } while(i < len && c1 == -1);
		  if(c1 == -1)
		      break;
		 
		  /* c2 */
		  do {
		      c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
		  } while(i < len && c2 == -1);
		  if(c2 == -1)
		      break;
		 
		  out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
		 
		  /* c3 */
		  do {
		      c3 = str.charCodeAt(i++) & 0xff;
		      if(c3 == 61)
		    return out;
		      c3 = base64DecodeChars[c3];
		  } while(i < len && c3 == -1);
		  if(c3 == -1)
		      break;
		 
		  out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
		 
		  /* c4 */
		  do {
		      c4 = str.charCodeAt(i++) & 0xff;
		      if(c4 == 61)
		    return out;
		      c4 = base64DecodeChars[c4];
		  } while(i < len && c4 == -1);
		  if(c4 == -1)
		      break;
		  out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
		    }
		    return out;
		}
		 
		if (!window.btoa) window.btoa = base64encode;
		if (!window.atob) window.atob = base64decode;
	})();
	</script>
</head>

<!-- Page passes the container for the graph to the program -->
<body onload="main(document.getElementById('graphContainer'))">

	<!-- Creates a container for the graph with a grid wallpaper -->
	<div id="graphContainer"
		style="position:relative;overflow:hidden;width:321px;height:241px;border:1px solid gray;cursor:default;">
	</div>
</body>
</html>
